#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

using namespace std;
const int charsize =1024;
const int gargvnum =64;
const int envnum =64;
//全局的
char* gargv[gargvnum];
int gargc;

char* genv[envnum];


//全局的当前shell的工作路径(定义到全局不会被销毁)
char pwd[charsize];
char pwdenv[charsize];

//全局的进程返回码
int lastcode = 0;

string GetUsrName()
{
    string name =getenv("USER");
    return name.empty()?  "None" : name;
}
string GetHostName()
{
    string name =getenv("HOSTNAME");
    return name.empty()?  "None" : name;
}
string GetPwd()
{   
    //string name =getenv("PWD");
    //return name.empty()?  "None" : name;
    

    //从pcb中直接拿pwd
    if(nullptr ==  getcwd(pwd,sizeof(pwd))) return "None";
    //拿到后还需要更新环境变量中的pwd
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);
    return pwd;
    

}
string MakeCommandLine()
{  //[root@hcss-ecs-1f3a lesson17]#  
    char CommandLine[charsize];
    snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\
        GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());
    return CommandLine;   
}
void PrintCommandLine()//1.打印命令行提示符
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}


bool GetCommand(char Command_buff[] ,int size)//2.获取命令
{
    //将命令输出到字符数组中
    //ls -a -l -n
    char*result =fgets(Command_buff,size,stdin);
    if(!result)
    {
        return false;
    }
    Command_buff[strlen(Command_buff)-1]= 0;//fgets会将回车(\n)也输入
    if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)会停下来
    return true; 
}

 void ParseCommand(char Command_buff[] ,int size)//3.分析命令
{
    memset(gargv ,0,sizeof(gargv));
    gargc=0;
    const char* SEP =" ";
    gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 会在字符串中查找分隔符，并将分隔符替换为 \0，从而将字符串分割成多个
    while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后续使用要用nullptr代替元字符串
    gargc--;
}

void debug()
{
    printf("argc: %d\n", gargc);
    for(int i = 0; gargv[i]; i++)
    {
        printf("argv[%d]: %s\n", i, gargv[i]);
    }
}



bool ExecuteCommand()//4.执行命令
{
    pid_t id =fork();
    if(id ==0)
    {//子进程
        int ret =execvpe(gargv[0],gargv,genv);
        if(ret ==-1) cout<<"子进程出错\n"<<endl;
        exit(1);
    }
    int status =0;
    pid_t rid =waitpid(id,&status ,0);
    if(rid >0)
    {
        if(WIFEXITED(status))//子进程正常的返回
        {  
           lastcode=WEXITSTATUS(status);      
        }
        else lastcode = 100;//子进程异常
        return true;
    } 
    return false;
    
}

void AddEnv(char* item)
{
    int index =0;
    while(genv[index])
    {
        index++;
    }
    genv[index]=(char*)malloc(strlen(item)+1);
    strncpy(genv[index] ,item,strlen(item)+1);
    genv[++index]= nullptr;
}
//内建命令的执行(调用函数,改变状态)
bool CheckandExecBuiltCommand()
{   //使用穷举法找内建命令
    if(0 == strcmp(gargv[0],"cd"))
    {
        if(gargc == 2)
        {
            chdir(gargv[1]);
            lastcode= 0;    
        }
        else
        {
            lastcode=1;
        }
        return true; 
    }
    else if(0 ==strcmp(gargv[0] ,"export")) 
    {
        if(gargc ==2)
        {
             AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
        {
            lastcode= 1;    
        }
        return true;
    }
    else if(0 == strcmp(gargv[0],"env"))
    {
        for(int i=0 ;genv[i];i++)
        {
            printf("%s\n",genv[i]);
            
        }
        lastcode =0;
        return true;
    }
    else if(0 ==strcmp(gargv[0] ,"echo"))
    {
        if(gargc ==2)
        {
            //echo $?
            //echo $PATH
            //echo hello
            if(gargv[1][0] == '$')
            {
                 if(gargv[1][1] == '?')
                 {
                    printf("%d\n",lastcode);
                    lastcode =0;   
                 }
            }
            else
            {
                printf("%s\n",gargv[1]);
                lastcode =0;   
            }
        }
        else
        {
            lastcode=1;
        }
        return true;
        
    }  
    return false; 
              
}

// 作为一个shell，获取环境变量应该从系统的配置来
//  我们今天就直接从父shell中获取环境变量   
void IniEnv()
{   
    extern char** environ;
    int index=0;
    while(environ[index])
    {
        genv[index]=(char*)malloc(strlen(environ[index])+1);//strlen不含\0
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;      
    }
    genv[index]= nullptr;
       
}

int main()
{   
    IniEnv();
    char Command_buff[charsize];
    while(true)
    {
        PrintCommandLine();//1.打印命令行提示符
        
        if(!GetCommand(Command_buff,charsize))//2.获取命令
        {
            continue;
        }
        ParseCommand(Command_buff ,charsize);//3.分析命令
        //debug();
        //判断是否是内建命令,shell自己执行
        if(CheckandExecBuiltCommand())//是内建命令并执行
        {
            continue;   
        }
        //不是内建命令,创建子进程执行
        ExecuteCommand();//4.执行命令
    }
    return 0;
}
